<!-- build time:Tue Nov 03 2020 20:13:59 GMT+0800 (GMT+08:00) --><!DOCTYPE html><html class="theme-next pisces use-motion" lang="zh-CN"><head><meta name="generator" content="Hexo 3.9.0"><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=2"><meta name="theme-color" content="#222"><link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/Han/3.3.0/han.min.css"><meta name="google-site-verification" content="U349XOYHFCgkEOYhTP4YlFqqyEq_f7zA8B4ERY7tWXA"><meta name="baidu-site-verification" content="APKb23AsyK"><link rel="stylesheet" href="//cdn.bootcss.com/fancybox/3.5.6/jquery.fancybox.min.css"><link rel="stylesheet" href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2"><link rel="stylesheet" href="/blog/css/main.css?v=7.1.2"><link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png?v=7.1.2"><link rel="icon" type="image/png" sizes="32x32" href="/blog/icos/favicon-48x48.ico?v=7.1.2"><link rel="icon" type="image/png" sizes="16x16" href="/blog/icos/favicon-32x32.ico?v=7.1.2"><link rel="mask-icon" href="/blog/images/logo.svg?v=7.1.2" color="#222"><script id="hexo.configurations">var NexT=window.NexT||{},CONFIG={root:"/blog/",scheme:"Pisces",version:"7.1.2",sidebar:{position:"left",display:"post",offset:12,onmobile:!1,dimmer:!1},back2top:!0,back2top_sidebar:!1,fancybox:!0,fastclick:!0,lazyload:!0,tabs:!0,motion:{enable:!0,async:!0,transition:{post_block:"fadeIn",post_header:"slideDownIn",post_body:"slideDownIn",coll_header:"slideLeftIn",sidebar:"slideUpIn"}},algolia:{applicationID:"",apiKey:"",indexName:"",hits:{per_page:10},labels:{input_placeholder:"Search for Posts",hits_empty:"We didn't find any results for the search: ${query}",hits_stats:"${hits} results found in ${time} ms"}}}</script><meta name="description" content="在正式介绍WebSocket之前先跟大家科普一下以及讨论一下过去是如何实现Web双向通信的科普一下通讯传输模式单工：只支持数据在一个方向上传输；例如：BP机半双工：允许数据在两个方向上传输，但是某一时刻只允许数据在一个方向上传输；例如：对讲机, 电报机全双工：同时在两个方向上传输，是两个单工通信的结合，要求发送设备和接收设备同时具有独立的接收和发送能力。 例如：手机"><meta name="keywords" content="WebSocket,js"><meta property="og:type" content="article"><meta property="og:title" content="WebSocket技术分享"><meta property="og:url" content="https://liliuzhu.gitee.io/blog/2019/06/Internal-technology-sharing-WebSocket.html"><meta property="og:site_name" content="Luther Li&#39;s Blog"><meta property="og:description" content="在正式介绍WebSocket之前先跟大家科普一下以及讨论一下过去是如何实现Web双向通信的科普一下通讯传输模式单工：只支持数据在一个方向上传输；例如：BP机半双工：允许数据在两个方向上传输，但是某一时刻只允许数据在一个方向上传输；例如：对讲机, 电报机全双工：同时在两个方向上传输，是两个单工通信的结合，要求发送设备和接收设备同时具有独立的接收和发送能力。 例如：手机"><meta property="og:locale" content="zh-CN"><meta property="og:image" content="https://liliuzhu.github.io/CDN/images/blog/2019/websocket-banner.jpg"><meta property="og:updated_time" content="2020-05-07T09:22:10.501Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="WebSocket技术分享"><meta name="twitter:description" content="在正式介绍WebSocket之前先跟大家科普一下以及讨论一下过去是如何实现Web双向通信的科普一下通讯传输模式单工：只支持数据在一个方向上传输；例如：BP机半双工：允许数据在两个方向上传输，但是某一时刻只允许数据在一个方向上传输；例如：对讲机, 电报机全双工：同时在两个方向上传输，是两个单工通信的结合，要求发送设备和接收设备同时具有独立的接收和发送能力。 例如：手机"><meta name="twitter:image" content="https://liliuzhu.github.io/CDN/images/blog/2019/websocket-banner.jpg"><link rel="alternate" href="/blog/atom.xml" title="Luther Li's Blog" type="application/atom+xml"><link rel="canonical" href="https://liliuzhu.gitee.io/blog/2019/06/Internal-technology-sharing-WebSocket"><script id="page.configurations">CONFIG.page={sidebar:""}</script><title>WebSocket技术分享 | Luther Li's Blog</title><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?2a46638d9d90d8e9e8bd06f78263ffa4";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><noscript><style>.sidebar-inner,.use-motion .brand,.use-motion .collection-title,.use-motion .comments,.use-motion .menu-item,.use-motion .motion-element,.use-motion .pagination,.use-motion .post-block,.use-motion .post-body,.use-motion .post-header{opacity:initial}.use-motion .logo,.use-motion .site-subtitle,.use-motion .site-title{opacity:initial;top:initial}.use-motion .logo-line-before i{left:initial}.use-motion .logo-line-after i{right:initial}</style></noscript></head><body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN"><div class="container sidebar-position-left page-post-detail"><div class="headband"></div><header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="header-inner"><div class="site-brand-wrapper"><div class="site-meta"><div class="custom-logo-site-title"><a href="/blog/" class="brand" rel="start"><span class="logo-line-before"><i></i></span> <span class="site-title">Luther Li's Blog</span> <span class="logo-line-after"><i></i></span></a></div><p class="site-subtitle">越努力，越精彩</p></div><div class="site-nav-toggle"><button aria-label="切换导航栏"><span class="btn-bar"></span> <span class="btn-bar"></span> <span class="btn-bar"></span></button></div></div><nav class="site-nav"><ul id="menu" class="menu"><li class="menu-item menu-item-home"><a href="/blog/" rel="section"><i class="menu-item-icon fa fa-fw fa-home"></i><br>首页</a></li><li class="menu-item menu-item-about"><a href="/blog/about/" rel="section"><i class="menu-item-icon fa fa-fw fa-user"></i><br>关于</a></li><li class="menu-item menu-item-tags"><a href="/blog/tags/" rel="section"><i class="menu-item-icon fa fa-fw fa-tags"></i><br>标签</a></li><li class="menu-item menu-item-categories"><a href="/blog/categories/" rel="section"><i class="menu-item-icon fa fa-fw fa-th"></i><br>分类</a></li><li class="menu-item menu-item-archives"><a href="/blog/archives/" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i><br>归档</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><i class="menu-item-icon fa fa-search fa-fw"></i><br>搜索</a></li></ul><div class="site-search"><div class="popup search-popup local-search-popup"><div class="local-search-header clearfix"><span class="search-icon"><i class="fa fa-search"></i> </span><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span><div class="local-search-input-wrapper"><input autocomplete="off" placeholder="搜索..." spellcheck="false" type="text" id="local-search-input"></div></div><div id="local-search-result"></div></div></div></nav></div></header><a href="https://github.com/liliuzhu" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewbox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"/><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin:130px 106px" class="octo-arm"/><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"/></svg></a><main id="main" class="main"><div class="main-inner"><div class="content-wrap"><div id="content" class="content"><div id="posts" class="posts-expand"><article class="post post-type-normal" itemscope itemtype="http://schema.org/Article"><div class="post-block"><link itemprop="mainEntityOfPage" href="https://liliuzhu.gitee.io/blog/blog/2019/06/Internal-technology-sharing-WebSocket.html"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="name" content="Luther Li"><meta itemprop="description" content="本人前端菜鸟一枚，踏在前端这条不归路上<br/>渐行渐远..."><meta itemprop="image" content="/blog/uploads/avatar.jpg"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="Luther Li's Blog"></span><header class="post-header"><h1 class="post-title" itemprop="name headline">WebSocket技术分享</h1><div class="post-meta"><span class="post-time"><span class="post-meta-item-icon"><i class="fa fa-calendar-o"></i> </span><span class="post-meta-item-text">发表于</span> <time title="创建时间：2019-06-27 17:01:07" itemprop="dateCreated datePublished" datetime="2019-06-27T17:01:07Z">2019-06-27</time> <span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-calendar-check-o"></i> </span><span class="post-meta-item-text">更新于</span> <time title="修改时间：2020-05-07 09:22:10" itemprop="dateModified" datetime="2020-05-07T09:22:10Z">2020-05-07</time> </span><span class="post-category"><span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-folder-o"></i> </span><span class="post-meta-item-text">分类于</span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/blog/categories/原创/" itemprop="url" rel="index"><span itemprop="name">原创</span></a></span> ， <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/blog/categories/前端/" itemprop="url" rel="index"><span itemprop="name">前端</span></a></span> ， <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/blog/categories/HTML5/" itemprop="url" rel="index"><span itemprop="name">HTML5</span></a></span> </span><span class="post-comments-count"><span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-comment-o"></i> </span><span class="post-meta-item-text">评论数：</span> <a href="/blog/2019/06/Internal-technology-sharing-WebSocket.html#comments" itemprop="discussionUrl"><span class="post-comments-count valine-comment-count" data-xid="/blog/2019/06/Internal-technology-sharing-WebSocket.html" itemprop="commentCount"></span> </a></span><span id="/blog/2019/06/Internal-technology-sharing-WebSocket.html" class="leancloud_visitors" data-flag-title="WebSocket技术分享"><span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-eye"></i> </span><span class="post-meta-item-text">阅读次数：</span> <span class="leancloud-visitors-count"></span></span><div class="post-symbolscount"><span class="post-meta-item-icon"><i class="fa fa-file-word-o"></i> </span><span class="post-meta-item-text">本文字数：</span> <span title="本文字数">16k</span> <span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-clock-o"></i> </span><span class="post-meta-item-text">阅读时长 &asymp;</span> <span title="阅读时长">27 分钟</span></div></div></header><div class="post-body han-init-context" itemprop="articleBody"><div class="post-gallery" itemscope itemtype="http://schema.org/ImageGallery"><div class="post-gallery-row"><img src="//liliuzhu.github.io/CDN/images/blog/2019/websocket-banner.jpg" itemprop="contentUrl"></div></div><p>在正式介绍WebSocket之前先跟大家科普一下以及讨论一下过去是如何实现Web双向通信的</p><h2 id="科普一下通讯传输模式"><a href="#科普一下通讯传输模式" class="headerlink" title="科普一下通讯传输模式"></a>科普一下通讯传输模式</h2><ul><li>单工：只支持数据在一个方向上传输；例如：BP机</li><li>半双工：允许数据在两个方向上传输，但是某一时刻只允许数据在一个方向上传输；例如：对讲机, 电报机</li><li>全双工：同时在两个方向上传输，是两个单工通信的结合，要求发送设备和接收设备同时具有独立的接收和发送能力。 例如：手机</li></ul><a id="more"></a><h2 id="历史回顾"><a href="#历史回顾" class="headerlink" title="历史回顾"></a>历史回顾</h2><p><img src="https://timgsa.baidu.com/timg?image&amp;quality=80&amp;size=b9999_10000&amp;sec=1554892663868&amp;di=70a4e5117385e2aaf276a6819eb7aabe&amp;imgtype=0&amp;src=http%3A%2F%2Fwww.th7.cn%2Fd%2Ffile%2Fp%2F2016%2F04%2F19%2F07950eab0b874e0c84eb7f0167c2bab7.jpg" alt="历史回顾示意图"></p><p>HTTP 协议有一个缺陷：通信只能由客户端发起。举例来说，我们想了解今天的天气，只能是客户端向服务器发出请求，服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。这种单向请求的特点，注定了如果服务器有连续的状态变化，客户端要获知就非常麻烦。 在WebSocket协议之前，有三种实现双向通信的方式：轮询（polling）、长轮询（long-polling）和iframe流（streaming）。</p><h3 id="轮询（polling）"><a href="#轮询（polling）" class="headerlink" title="轮询（polling）"></a>轮询（polling）</h3><p><img src="http://zuccess.cn/upload/img/20150114/201501140947113792.jpg" alt="轮询示意图"><br><img src="//liliuzhu.github.io/CDN/images/blog/2019/16a10769c0e4d72b.jpg" alt="轮询示意图"></p><p>轮询是客户端和服务器之间会一直进行连接，每隔一段时间就询问一次。其缺点也很明显：连接数会很多，一个接受，一个发送。而且 每次发送请求都会有Http的Header，会很耗流量，也会消耗CPU的利用率 。</p><ul><li>优点：实现简单，无需做过多的更改</li><li>缺点：轮询的间隔过长，会导致用户不能及时接收到更新的数据；轮询的间隔过短，会导致查询请求过多，增加服务器端的负担</li></ul><p><strong>实例</strong></p><p>1.index.html<br></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--index.html--&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"https://unpkg.com/axios@0.18.0/dist/axios.min.js"</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"https://unpkg.com/vue@2.6.10/dist/vue.min.js"</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>polling<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"polling"</span>&gt;</span>http 轮询<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"stopPolling"</span>&gt;</span>停止轮询<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>&#123;&#123;time&#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">    <span class="built_in">window</span>.onload=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span></span><br><span class="line"><span class="javascript">        <span class="keyword">let</span> vm=<span class="keyword">new</span> Vue(&#123;</span></span><br><span class="line"><span class="actionscript">            el:<span class="string">'#app'</span>,</span></span><br><span class="line">            data:&#123;</span><br><span class="line"><span class="actionscript">                time: <span class="string">''</span>,</span></span><br><span class="line"><span class="actionscript">                timer: <span class="literal">null</span></span></span><br><span class="line">            &#125;,</span><br><span class="line">            mounted() &#123;</span><br><span class="line"></span><br><span class="line">            &#125;,</span><br><span class="line">            methods: &#123;</span><br><span class="line">                polling() &#123;</span><br><span class="line"><span class="actionscript">                    <span class="keyword">this</span>.stopPolling()</span></span><br><span class="line"><span class="actionscript">                    <span class="keyword">this</span>.timer = setInterval(<span class="keyword">this</span>.getTime, <span class="number">1000</span>)</span></span><br><span class="line">                &#125;,</span><br><span class="line">                stopPolling() &#123;</span><br><span class="line"><span class="actionscript">                    clearInterval(<span class="keyword">this</span>.timer)</span></span><br><span class="line"><span class="actionscript">                    <span class="keyword">this</span>.timer = <span class="literal">null</span></span></span><br><span class="line">                &#125;,</span><br><span class="line">                getTime()&#123;</span><br><span class="line"><span class="javascript">                    <span class="built_in">window</span>.axios.get(<span class="string">'/polling'</span>).then(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="actionscript">                        <span class="keyword">this</span>.time = res.data</span></span><br><span class="line">                    &#125;)</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>2.server.js<br></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// server.js</span></span><br><span class="line"><span class="keyword">const</span> port = <span class="number">8001</span></span><br><span class="line"><span class="keyword">let</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"><span class="keyword">let</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>), <span class="comment">//引入express模块</span></span><br><span class="line">   app = express(),</span><br><span class="line">   server = <span class="built_in">require</span>(<span class="string">'http'</span>).createServer(app);</span><br><span class="line">app.use(express.static(path.join(__dirname, <span class="string">'static'</span>))); <span class="comment">//指定静态HTML文件的位置</span></span><br><span class="line">app.get(<span class="string">'/polling'</span>,<span class="function"><span class="keyword">function</span>(<span class="params">req,res</span>)</span>&#123;</span><br><span class="line">    res.end(<span class="keyword">new</span> <span class="built_in">Date</span>().toLocaleString());</span><br><span class="line">&#125;);</span><br><span class="line">server.listen(port);</span><br><span class="line">server.setTimeout(<span class="number">0</span>);   <span class="comment">//设置不超时，所以服务端不会主动关闭连接</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'server started'</span>, <span class="string">'http://127.0.0.1:'</span> + port);</span><br></pre></td></tr></table></figure><p></p><p>3.效果图<br><img src="//liliuzhu.github.io/CDN/images/blog/2019/websocket-polling.gif" alt="实例效果图"></p><h3 id="长轮询（long-polling）"><a href="#长轮询（long-polling）" class="headerlink" title="长轮询（long-polling）"></a>长轮询（long-polling）</h3><p><img src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1472113332,2688476676&amp;fm=26&amp;gp=0.jpg" alt="长轮询示意图"></p><p>长轮询是对轮询的改进版，客户端发送HTTP给服务器之后，看有没有新消息，如果没有新消息，就一直等待。当有新消息的时候，才会返回给客户端。在某种程度上减小了网络带宽和CPU利用率等问题。由于http数据包的头部数据量往往很大（通常有400多个字节），但是真正被服务器需要的数据却很少（有时只有10个字节左右），这样的数据包在网络上周期性的传输，难免 对网络带宽是一种浪费 。</p><ul><li>优点：比 Polling 做了优化，有较好的时效性</li><li>缺点：保持连接会消耗资源; 服务器没有返回有效数据，程序超时。</li></ul><p><strong>实例</strong></p><p>1.index.html<br></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--index.html--&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"https://unpkg.com/axios@0.18.0/dist/axios.min.js"</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"https://unpkg.com/vue@2.6.10/dist/vue.min.js"</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>long-polling<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"longPolling"</span>&gt;</span>http 长轮询<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"stopPolling"</span>&gt;</span>停止轮询<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>&#123;&#123;time&#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">    <span class="built_in">window</span>.onload=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span></span><br><span class="line"><span class="javascript">        <span class="keyword">let</span> vm=<span class="keyword">new</span> Vue(&#123;</span></span><br><span class="line"><span class="actionscript">            el:<span class="string">'#app'</span>,</span></span><br><span class="line">            data:&#123;</span><br><span class="line"><span class="actionscript">                time: <span class="string">''</span>,</span></span><br><span class="line"><span class="actionscript">                timer: <span class="literal">null</span></span></span><br><span class="line">            &#125;,</span><br><span class="line">            methods: &#123;</span><br><span class="line">                stopPolling() &#123;</span><br><span class="line"><span class="actionscript">                    <span class="keyword">this</span>.timer = <span class="literal">null</span></span></span><br><span class="line">                &#125;,</span><br><span class="line">                longPolling() &#123;</span><br><span class="line"><span class="actionscript">                    <span class="keyword">if</span>(!<span class="keyword">this</span>.timer)&#123;</span></span><br><span class="line"><span class="actionscript">                        <span class="keyword">this</span>.timer = <span class="literal">true</span></span></span><br><span class="line"><span class="actionscript">                        <span class="keyword">this</span>.getTime()</span></span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;,</span><br><span class="line">                getTime()&#123;</span><br><span class="line"><span class="javascript">                    <span class="built_in">window</span>.axios.get(<span class="string">'/longPolling'</span>, &#123;<span class="attr">timeout</span>: <span class="number">5000</span>&#125;).then(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="actionscript">                        <span class="keyword">this</span>.time = res.data</span></span><br><span class="line"><span class="actionscript">                        <span class="keyword">this</span>.timer &amp;&amp; <span class="keyword">this</span>.getTime()</span></span><br><span class="line"><span class="javascript">                    &#125;).catch(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="javascript">                        <span class="built_in">console</span>.log(err)</span></span><br><span class="line"><span class="actionscript">                        <span class="keyword">this</span>.timer &amp;&amp; <span class="keyword">this</span>.getTime()</span></span><br><span class="line">                    &#125;)</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>2.server.js<br></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// server.js</span></span><br><span class="line"><span class="keyword">const</span> port = <span class="number">8001</span></span><br><span class="line"><span class="keyword">let</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"><span class="keyword">let</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>), <span class="comment">//引入express模块</span></span><br><span class="line">   app = express(),</span><br><span class="line">   server = <span class="built_in">require</span>(<span class="string">'http'</span>).createServer(app);</span><br><span class="line">app.use(express.static(path.join(__dirname, <span class="string">'static'</span>))); <span class="comment">//指定静态HTML文件的位置</span></span><br><span class="line">app.get(<span class="string">'/longPolling'</span>,<span class="function"><span class="keyword">function</span>(<span class="params">req,res</span>)</span>&#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="params">_</span> =&gt;</span> &#123;</span><br><span class="line">        res.end(<span class="keyword">new</span> <span class="built_in">Date</span>().toLocaleString());</span><br><span class="line">    &#125;, <span class="number">1000</span>)</span><br><span class="line">&#125;);</span><br><span class="line">server.listen(port);</span><br><span class="line">server.setTimeout(<span class="number">0</span>);   <span class="comment">//设置不超时，所以服务端不会主动关闭连接</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'server started'</span>, <span class="string">'http://127.0.0.1:'</span> + port);</span><br></pre></td></tr></table></figure><p></p><p>3.效果图<br><img src="//liliuzhu.github.io/CDN/images/blog/2019/websocket-stopPolling.gif" alt="实例效果图"></p><h3 id="长连接"><a href="#长连接" class="headerlink" title="长连接"></a>长连接</h3><p><img src="//liliuzhu.github.io/CDN/images/blog/2019/16a10752c13bb601.jpg" alt="长连接示意图"></p><h4 id="iframe流（streaming）"><a href="#iframe流（streaming）" class="headerlink" title="iframe流（streaming）"></a>iframe流（streaming）</h4><p>iframe流方式是在页面中插入一个隐藏的iframe，利用其src属性在服务器和客户端之间创建一条长连接，服务器向iframe传输数据（通常是HTML，内有负责插入信息的javascript），来实时更新页面。</p><ul><li>优点：消息能够实时到达；浏览器兼容好</li><li>缺点：服务器维护一个长连接会增加开销；非动态设置iframe.srec时IE、chrome、Firefox会显示加载没有完成，图标会不停旋转,见下面两图</li></ul><p><strong>实例</strong></p><p>1.index.html<br></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--index.html--&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>longConnection<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"longConnection()"</span>&gt;</span>http 长连接<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"stopLongConnection()"</span>&gt;</span>关闭长连接<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span> <span class="attr">id</span>=<span class="string">"longConnection"</span>&gt;</span><span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">iframe</span> <span class="attr">id</span>=<span class="string">"iframe"</span> <span class="attr">src</span>=<span class="string">""</span> <span class="attr">style</span>=<span class="string">"display:none"</span>&gt;</span><span class="tag">&lt;/<span class="name">iframe</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">    <span class="keyword">var</span> iframe = <span class="built_in">document</span>.getElementById(<span class="string">'iframe'</span>)</span></span><br><span class="line"><span class="actionscript">    <span class="function"><span class="keyword">function</span> <span class="title">longConnection</span><span class="params">()</span> </span>&#123;</span></span><br><span class="line"><span class="actionscript">        iframe.src=<span class="string">'/longConnection2'</span></span></span><br><span class="line"><span class="javascript">        <span class="built_in">console</span>.log(iframe)</span></span><br><span class="line">    &#125;</span><br><span class="line"><span class="actionscript">    <span class="function"><span class="keyword">function</span> <span class="title">stopLongConnection</span><span class="params">()</span> </span>&#123;</span></span><br><span class="line"><span class="actionscript">        iframe.src=<span class="string">'/'</span></span></span><br><span class="line">    &#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>2.server.js<br></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// server.js</span></span><br><span class="line"><span class="keyword">const</span> port = <span class="number">8001</span></span><br><span class="line"><span class="keyword">let</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"><span class="keyword">let</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>), <span class="comment">//引入express模块</span></span><br><span class="line">   app = express(),</span><br><span class="line">   server = <span class="built_in">require</span>(<span class="string">'http'</span>).createServer(app);</span><br><span class="line">app.use(express.static(path.join(__dirname, <span class="string">'static'</span>))); <span class="comment">//指定静态HTML文件的位置</span></span><br><span class="line">app.get(<span class="string">'/longConnection2'</span>,<span class="function"><span class="keyword">function</span>(<span class="params">req,res</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> count = <span class="number">0</span></span><br><span class="line">    <span class="keyword">let</span> longConnectionTimer = <span class="literal">null</span></span><br><span class="line">    clearInterval(longConnectionTimer)</span><br><span class="line">    longConnectionTimer = setInterval(<span class="function"><span class="params">_</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (res.socket._handle) &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'longConnection2-'</span> + count++)</span><br><span class="line">            <span class="keyword">let</span> date = <span class="keyword">new</span> <span class="built_in">Date</span>().toLocaleString()</span><br><span class="line">            res.write(<span class="string">`</span></span><br><span class="line"><span class="string">           &lt;script type="text/javascript"&gt;</span></span><br><span class="line"><span class="string">             parent.document.getElementById('longConnection').innerHTML = "<span class="subst">$&#123;date&#125;</span>";//改变父窗口dom元素</span></span><br><span class="line"><span class="string">           &lt;/script&gt;</span></span><br><span class="line"><span class="string">         `</span>)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'longConnection2-stop'</span>)</span><br><span class="line">            clearInterval(longConnectionTimer)</span><br><span class="line">            longConnectionTimer = <span class="literal">null</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="number">1000</span>)</span><br><span class="line">&#125;);</span><br><span class="line">server.listen(port);</span><br><span class="line">server.setTimeout(<span class="number">0</span>);   <span class="comment">//设置不超时，所以服务端不会主动关闭连接</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'server started'</span>, <span class="string">'http://127.0.0.1:'</span> + port);</span><br></pre></td></tr></table></figure><p></p><p>3.效果图<br><img src="//liliuzhu.github.io/CDN/images/blog/2019/websocket-longConnection2-3.gif" alt="实例效果图1"><br><img src="//liliuzhu.github.io/CDN/images/blog/2019/websocket-longConnection2.gif" alt="实例效果图2"></p><h4 id="事件流-EventSource（SSE-Server-Sent-Events，不能算作历史技术，属于H5范围）"><a href="#事件流-EventSource（SSE-Server-Sent-Events，不能算作历史技术，属于H5范围）" class="headerlink" title="事件流 EventSource（SSE - Server-Sent Events，不能算作历史技术，属于H5范围）"></a>事件流 EventSource（SSE - Server-Sent Events，不能算作历史技术，属于H5范围）</h4><p>EventSource的官方名称应该是Server-sent events (SSE)服务端派发事件，EventSource 基于http协议只是简单的单项通信，实现了服务端推的过程客户端无法通过EventSource向服务端发送数据。虽然不能实现双向通信但是在功能设计上他也有一些优点比如可以自动重连接,event-IDs,以及发送随机事件的能力（WebSocket要借助第三方库比如socket.io可以实现重连。）<br><strong>实例</strong></p><p>1.index.html<br></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--index.html--&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"https://unpkg.com/vue@2.6.10/dist/vue.min.js"</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>polling<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"longConnection"</span>&gt;</span>http 长连接<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"stopLongConnection"</span>&gt;</span>关闭长连接<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>&#123;&#123;time&#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">    <span class="built_in">window</span>.onload=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span></span><br><span class="line"><span class="javascript">        <span class="keyword">let</span> vm=<span class="keyword">new</span> Vue(&#123;</span></span><br><span class="line"><span class="actionscript">            el:<span class="string">'#app'</span>,</span></span><br><span class="line">            data:&#123;</span><br><span class="line"><span class="actionscript">                time: <span class="string">''</span>,</span></span><br><span class="line"><span class="actionscript">                eventSource: <span class="literal">null</span></span></span><br><span class="line">            &#125;,</span><br><span class="line">            methods: &#123;</span><br><span class="line">                stopLongConnection() &#123;</span><br><span class="line"><span class="actionscript">                   <span class="keyword">this</span>.close()</span></span><br><span class="line">                &#125;,</span><br><span class="line">                longConnection() &#123;</span><br><span class="line"><span class="actionscript">                    <span class="keyword">this</span>.getTime()</span></span><br><span class="line">                &#125;,</span><br><span class="line">                getTime()&#123;</span><br><span class="line"><span class="actionscript">                    <span class="comment">// 实例化 EventSource 对象，并指定一个 URL 地址</span></span></span><br><span class="line"><span class="actionscript">                    <span class="keyword">this</span>.eventSource = <span class="keyword">new</span> EventSource(<span class="string">'/longConnection'</span>); <span class="comment">// 使用 addEventListener() 方法监听事件</span></span></span><br><span class="line"><span class="javascript">                    <span class="built_in">console</span>.log(<span class="string">"当前状态0"</span>, <span class="keyword">this</span>.eventSource.readyState);</span></span><br><span class="line"><span class="actionscript">                    <span class="keyword">this</span>.eventSource.onopen = <span class="keyword">this</span>.onopen</span></span><br><span class="line"><span class="actionscript">                    <span class="keyword">this</span>.eventSource.onmessage = <span class="keyword">this</span>.onmessage</span></span><br><span class="line"><span class="actionscript">                    <span class="keyword">this</span>.eventSource.onerror = <span class="keyword">this</span>.onerror</span></span><br><span class="line">                &#125;,</span><br><span class="line">                onopen()&#123;</span><br><span class="line"><span class="javascript">                    <span class="built_in">console</span>.log(<span class="string">"链接成功."</span>);</span></span><br><span class="line"><span class="javascript">                    <span class="built_in">console</span>.log(<span class="string">"当前状态1"</span>, <span class="keyword">this</span>.eventSource.readyState);</span></span><br><span class="line">                &#125;,</span><br><span class="line">                onmessage(res)&#123;</span><br><span class="line"><span class="actionscript">                    <span class="keyword">this</span>.time = res.data</span></span><br><span class="line">                &#125;,</span><br><span class="line">                onerror(err)&#123;</span><br><span class="line"><span class="javascript">                    <span class="built_in">console</span>.log(err)</span></span><br><span class="line">                &#125;,</span><br><span class="line">                close()&#123;</span><br><span class="line"><span class="actionscript">                    <span class="keyword">this</span>.eventSource &amp;&amp; <span class="keyword">this</span>.eventSource.close()</span></span><br><span class="line"><span class="javascript">                    <span class="built_in">console</span>.log(<span class="string">"当前状态2"</span>, <span class="keyword">this</span>.eventSource.readyState);</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>2.server.js<br></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// server.js</span></span><br><span class="line"><span class="keyword">const</span> port = <span class="number">8001</span></span><br><span class="line"><span class="keyword">let</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"><span class="keyword">let</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>), <span class="comment">//引入express模块</span></span><br><span class="line">   app = express(),</span><br><span class="line">   server = <span class="built_in">require</span>(<span class="string">'http'</span>).createServer(app);</span><br><span class="line">app.use(express.static(path.join(__dirname, <span class="string">'static'</span>))); <span class="comment">//指定静态HTML文件的位置</span></span><br><span class="line">app.get(<span class="string">'/longConnection'</span>,<span class="function"><span class="keyword">function</span>(<span class="params">req,res</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> count = <span class="number">0</span></span><br><span class="line">    <span class="keyword">let</span> longConnectionTimer = <span class="literal">null</span></span><br><span class="line">    clearInterval(longConnectionTimer)</span><br><span class="line">    res.writeHead(<span class="number">200</span>, &#123;</span><br><span class="line">        <span class="string">'Content-Type'</span>: <span class="string">"text/event-stream"</span>,</span><br><span class="line">        <span class="string">'Cache-Control'</span>: <span class="string">'no-cache'</span>,</span><br><span class="line">        <span class="string">'Connection'</span>: <span class="string">'keep-alive'</span></span><br><span class="line">    &#125;)</span><br><span class="line">    longConnectionTimer = setInterval(<span class="function"><span class="params">_</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(res.socket._handle)&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'longConnection-'</span> + count++)</span><br><span class="line">            <span class="keyword">const</span> data = &#123; <span class="attr">timeStamp</span>: <span class="built_in">Date</span>.now() &#125;;</span><br><span class="line">            res.write(<span class="string">`data: <span class="subst">$&#123;<span class="keyword">new</span> <span class="built_in">Date</span>().toLocaleString()&#125;</span>\n\n`</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'longConnection-stop'</span>)</span><br><span class="line">            clearInterval(longConnectionTimer)</span><br><span class="line">            longConnectionTimer = <span class="literal">null</span></span><br><span class="line">            res.end(<span class="string">'stop'</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="number">1000</span>)</span><br><span class="line">&#125;);</span><br><span class="line">server.listen(port);</span><br><span class="line">server.setTimeout(<span class="number">0</span>);   <span class="comment">//设置不超时，所以服务端不会主动关闭连接</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'server started'</span>, <span class="string">'http://127.0.0.1:'</span> + port);</span><br></pre></td></tr></table></figure><p></p><p>3.效果图<br><img src="//liliuzhu.github.io/CDN/images/blog/2019/websocket-longConnection.gif" alt="实例效果图"></p><p><strong>有什么用</strong>：<br>因为受单项通信的限制EventSource非常适应于后端数据更新频繁且对实时性要求较高而又不需要客户端向服务端通信的场景下。比如来实现像股票报价、新闻推送、实时天气这些只需要服务器发送消息给客户端场景中。EventSource的使用更加便捷这也是他的优点。</p><p>EventSource的应用，webpack-hot-middleware原理</p><ul><li>优点<ol><li>基于现有http协议，实现简单</li><li>断开后自动重联，并可设置重联超时</li><li>派发任意事件</li><li>跨域并有相应的安全过滤</li></ol></li><li>缺点<ol><li>只能单向通信，服务器端向客户端推送事件</li><li>事件流协议只能传输UTF-8数据，不支持二进制流。</li><li>兼容性不高，IE 和 Edge 下目前所有不支持EventSource</li><li>服务器端需要保持 HTTP 连接，消耗一定的资源</li></ol></li></ul><p>EventSource实例的readyState属性，表明连接的当前状态。该属性只读，可以取以下值。</p><ul><li>0：相当于常量EventSource.CONNECTING，表示连接还未建立，或者断线正在重连。</li><li>1：相当于常量EventSource.OPEN，表示连接已经建立，可以接受数据。</li><li>2：相当于常量EventSource.CLOSED，表示连接已断，且不会重连。</li></ul><p><strong>注意:</strong></p><ol><li>EventSource是一种服务端推送技术。</li><li>一般来说，网页都是通过发送请求从服务端获取数据，而服务端推送技术 使服务器随时可以向客户端发送数据。</li><li>EventSource基于http长链接<ul><li>客户端需要创建一个EventSource对象，服务端URI为参数</li><li>服务端返回的响应报文的Content-Type须为text/event-stream。</li></ul></li></ol><h3 id="Flash-Socket"><a href="#Flash-Socket" class="headerlink" title="Flash Socket"></a>Flash Socket</h3><p>在页面中内嵌入一个使用了Socket类的Flash程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信，JavaScript在收到服务器端传送的信息后控制页面的显示。</p><ul><li>优点：实现真正的即时通信，而不是伪即时。</li><li>缺点：客户端必须安装Flash插件；非HTTP协议，无法自动穿越防火墙。</li><li>实例：网络互动游戏。</li></ul><p>==<strong>Flash 不懂也不说太多了，再多说都是瞎编了</strong>==</p><p>以上demo源码地址：<a href="https://github.com/liliuzhu/personalShareDemo/tree/master/WebSocket/polling" target="_blank" rel="noopener">https://github.com/liliuzhu/personalShareDemo/tree/master/WebSocket/polling</a></p><h2 id="WebSocket"><a href="#WebSocket" class="headerlink" title="WebSocket"></a>WebSocket</h2><p>WebSocket是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议。</p><p>WebSocket使得客户端和服务器之间的数据交换变得更加简单，允许服务端主动向客户端推送数据。在WebSocket API中，浏览器和服务器只需要完成一次握手，两者之间就直接可以创建持久性的连接，并进行双向数据传输。</p><p>在WebSocket API中，浏览器和服务器只需要做一个握手的动作，然后，浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。</p><p>HTML5 定义的 WebSocket协议，能更好的节省服务器资源和带宽，并且能够更实时地进行通讯；解决了轮询以及其他长连接的很多缺点。<br><img src="http://www.runoob.com/wp-content/uploads/2016/03/ws.png" alt="对比示意图"></p><h3 id="如何使用-WebSocket"><a href="#如何使用-WebSocket" class="headerlink" title="如何使用 WebSocket"></a>如何使用 WebSocket</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// WebSocket的客户端原生api</span></span><br><span class="line"><span class="keyword">var</span> Socket = <span class="keyword">new</span> WebSocket(<span class="string">'ws://localhost:8080'</span>) <span class="comment">// WebSocket 对象作为一个构造函数，用于新建 WebSocket 实例。</span></span><br><span class="line">Socket.onopen = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125; <span class="comment">// 连接建立时触发</span></span><br><span class="line">Socket.onclose = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125;  <span class="comment">// 连接关闭时触发</span></span><br><span class="line">Socket.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125; <span class="comment">// 客户端接收服务端数据时触发</span></span><br><span class="line">Socket.send(<span class="string">'data'</span>) <span class="comment">// 实例对象的send()方法用于向服务器发送数据</span></span><br><span class="line">Socket.close() <span class="comment">// 关闭连接</span></span><br><span class="line">socket.onerror = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125; <span class="comment">// 通信发生错误时触发</span></span><br></pre></td></tr></table></figure><p>Socket.readyState 表示连接状态，可以是以下值</p><ul><li>0 - 表示连接尚未建立。</li><li>1 - 表示连接已建立，可以进行通信。</li><li>2 - 表示连接正在进行关闭。</li><li>3 - 表示连接已经关闭或者连接不能打开。</li></ul><p><strong>注意：</strong><br>Websocket 使用ws或wss的统一资源标志符，类似于HTTPS，其中wss表示在TLS之上的Websocket</p><p>Websocket 使用和HTTP相同的TCP端口，可以绕过大多数防火墙的限制。默认情况下，Websocket 协议使用 80 端口；运行在 TLS 之上时，默认使用 443 端口。</p><p>虽然 WebSocketServer 可以使用别的端口，但是统一端口还是更优的选择</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 服务器数据可能是文本，也可能是二进制数据（blob对象或Arraybuffer对象）。</span></span><br><span class="line">ws.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="keyword">typeof</span> event.data === <span class="built_in">String</span>) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"Received data string"</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span>(event.data <span class="keyword">instanceof</span> <span class="built_in">ArrayBuffer</span>)&#123;</span><br><span class="line">    <span class="keyword">var</span> buffer = event.data;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"Received arraybuffer"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 除了动态判断收到的数据类型，也可以使用binaryType属性，显式指定收到的二进制数据类型。</span></span><br><span class="line"><span class="comment">// 收到的是 blob 数据</span></span><br><span class="line">ws.binaryType = <span class="string">"blob"</span>;</span><br><span class="line">ws.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(e.data.size);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 收到的是 ArrayBuffer 数据</span></span><br><span class="line">ws.binaryType = <span class="string">"arraybuffer"</span>;</span><br><span class="line">ws.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(e.data.byteLength);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 发送 Blob 对象的例子。</span></span><br><span class="line"><span class="keyword">var</span> file = <span class="built_in">document</span></span><br><span class="line">  .querySelector(<span class="string">'input[type="file"]'</span>)</span><br><span class="line">  .files[<span class="number">0</span>];</span><br><span class="line">ws.send(file);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 发送 ArrayBuffer 对象的例子。</span></span><br><span class="line"><span class="comment">// Sending canvas ImageData as ArrayBuffer</span></span><br><span class="line"><span class="keyword">var</span> img = canvas_context.getImageData(<span class="number">0</span>, <span class="number">0</span>, <span class="number">400</span>, <span class="number">320</span>);</span><br><span class="line"><span class="keyword">var</span> binary = <span class="keyword">new</span> <span class="built_in">Uint8Array</span>(img.data.length);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; img.data.length; i++) &#123;</span><br><span class="line">  binary[i] = img.data[i];</span><br><span class="line">&#125;</span><br><span class="line">ws.send(binary.buffer);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 实例对象的bufferedAmount属性，表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。</span></span><br><span class="line"><span class="keyword">var</span> data = <span class="keyword">new</span> <span class="built_in">ArrayBuffer</span>(<span class="number">10000000</span>);</span><br><span class="line">socket.send(data);</span><br><span class="line"><span class="keyword">if</span> (socket.bufferedAmount === <span class="number">0</span>) &#123;</span><br><span class="line">  <span class="comment">// 发送完毕</span></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  <span class="comment">// 发送还没结束</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="WebSocket-amp-EventSource-的区别"><a href="#WebSocket-amp-EventSource-的区别" class="headerlink" title="WebSocket &amp; EventSource 的区别"></a>WebSocket &amp; EventSource 的区别</h4><p>EventSource和WebSocket一样都是HTML5中的新技术,不过两者在定位上有很大的差别。</p><ol><li>WebSocket基于TCP协议，EventSource基于http协议。</li><li>EventSource是单向通信，而websocket是双向通信。</li><li>EventSource只能发送文本，而websocket支持发送二进制数据。</li><li>在实现上EventSource比websocket更简单。</li><li>EventSource有自动重连接（不借助第三方）以及发送随机事件的能力。</li><li>websocket的资源占用过大EventSource更轻量。</li><li>websocket可以跨域，EventSource基于http跨域需要服务端设置请求头。</li></ol><p>WebSocket 协议本质上是一个基于 TCP 的协议。</p><p>为了建立一个 WebSocket连接，客户端浏览器首先要向服务器发起一个HTTP请求，这个请求和通常的HTTP请求不同，包含了一些附加头信息，其中附加头信息”Upgrade:WebSocket”表明这是一个申请协议升级的 HTTP 请求，服务器端解析这些附加的头信息然后产生应答信息返回给客户端，客户端和服务器端的 WebSocket 连接就建立起来了，双方就可以通过这个连接通道自由的传递信息，并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。</p><p>Websocket 其实是一个新协议，跟HTTP协议基本没有关系，只是为了兼容现有浏览器的握手规范而已，也就是说它是 HTTP 协议上的一种补充。</p><p><img src="http://www.ruanyifeng.com/blogimg/asset/2017/bg2017051503.jpg" alt="说明示意图"></p><p>废话不说上案例<br><img src="//liliuzhu.github.io/CDN/images/blog/2019/websocket-chat.gif" alt="demo效果图"><br>以上demo源码地址： <a href="https://github.com/liliuzhu/personalShareDemo/tree/master/WebSocket/webSocket" target="_blank" rel="noopener">https://github.com/liliuzhu/personalShareDemo/tree/master/WebSocket/webSocket</a></p><h3 id="Web-实时推送技术的比较"><a href="#Web-实时推送技术的比较" class="headerlink" title="Web 实时推送技术的比较"></a>Web 实时推送技术的比较</h3><table><thead><tr><th>方式</th><th>类型</th><th>技术实现</th><th>优点</th><th>缺点</th><th>适用场景</th></tr></thead><tbody><tr><td>轮询Polling</td><td>client⇌server</td><td>客户端循环请求</td><td>1、实现简单 2、 支持跨域</td><td>1、浪费带宽和服务器资源 2、 一次请求信息大半是无用（完整http头信息） 3、有延迟 4、大部分无效请求</td><td>适于小型应用</td></tr><tr><td>长轮询Long-Polling</td><td>client⇌server</td><td>服务器hold住连接，一直到有数据或者超时才返回，减少重复请求次数</td><td>1、实现简单 2、不会频繁发请求 3、节省流量 4、延迟低</td><td>1、服务器hold住连接，会消耗资源 2、一次请求信息大半是无用</td><td>WebQQ、Hi网页版、Facebook IM</td></tr><tr><td>长连接iframe</td><td>server⇌client</td><td>在页面里嵌入一个隐蔵iframe，将这个 iframe 的 src 属性设为对一个长连接的请求，服务器端就能源源不断地往客户端输入数据。</td><td>1、数据实时送达 2、不发无用请求，一次链接，多次“推送”</td><td>1、服务器增加开销 2、无法准确知道连接状态 3、IE、chrome等一直会处于loading状态</td><td>Gmail聊天</td></tr><tr><td>EventSource</td><td>server→client</td><td>new EventSource()</td><td>1、基于现有http协议，实现简单2、断开后自动重联，并可设置重联超时3、派发任意事件4、跨域并有相应的安全过滤</td><td>1、只能单向通信，服务器端向客户端推送事件2、事件流协议只能传输UTF-8数据，不支持二进制流。4、兼容性不高，IE 和 Edge下目前所有不支持EventSource服务器端需要保持 HTTP 连接，消耗一定的资源</td><td>股票报价、新闻推送、实时天气</td><td></td></tr><tr><td>WebSocket</td><td>server⇌client</td><td>new WebSocket()</td><td>1、支持双向通信，实时性更强 2、可发送二进制文件3、减少通信量</td><td>1、浏览器支持程度不一致 2、不支持断开重连</td><td>网络游戏、银行交互和支付</td></tr></tbody></table><p>综上所述：Websocket协议不仅解决了HTTP协议中服务端的被动性，即通信只能由客户端发起，也解决了数据同步有延迟的问题，同时还带来了明显的性能优势，所以websocket是Web 实时推送技术的比较理想的方案，但如果要兼容低版本浏览器，可以考虑用轮询来实现。</p><h3 id="服务端的WebSocket"><a href="#服务端的WebSocket" class="headerlink" title="服务端的WebSocket"></a>服务端的WebSocket</h3><p>npm上有很多包对websocket做了实现比如 socket.io、WebSocket-Node、ws、nodejs-websocket还有很多</p><ol><li><p>Socket.io：<br>Socket.io是一个WebSocket库，包括了客户端的js和服务器端的nodejs，它会自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用（不支持WebSocket的情况会降级到AJAX轮询），非常方便和人性化，兼容性非常好，支持的浏览器最低达IE5.5。屏蔽了细节差异和兼容性问题，实现了跨浏览器/跨设备进行双向数据通信。</p></li><li><p>ws：<br>不像 socket.io 模块，ws是一个单纯的websocket模块，不提供向上兼容，不需要在客户端挂额外的js文件。在客户端不需要使用二次封装的api使用浏览器的原生Websocket API即可通信。</p></li></ol><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><ol><li><a href="https://blog.csdn.net/yhb241/article/details/79713830" target="_blank" rel="noopener">https://blog.csdn.net/yhb241/article/details/79713830</a></li><li><a href="https://www.tuicool.com/articles/FFFrUvy" target="_blank" rel="noopener">https://www.tuicool.com/articles/FFFrUvy</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Server-sent_events/EventSource" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Server-sent_events/EventSource</a></li><li><a href="https://www.jianshu.com/p/958eba34a5da" target="_blank" rel="noopener">https://www.jianshu.com/p/958eba34a5da</a></li><li><a href="http://www.runoob.com/html/html5-websocket.html" target="_blank" rel="noopener">http://www.runoob.com/html/html5-websocket.html</a></li></ol><blockquote><p>本文首发于个人技术博客 <a href="https://liliuzhu.gitee.io/blog">https://liliuzhu.gitee.io/blog</a></p></blockquote></div><div><div id="reward-container"><div>坚持技术分享，您的支持将鼓励我继续努力！</div><button id="reward-button" disable="enable" onclick="var qr = document.getElementById(&quot;qr&quot;); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">打赏</button><div id="qr" style="display:none"><div style="display:inline-block"><img src="/blog/uploads/wechatpay.jpg" alt="Luther Li 微信支付"><p>微信支付</p></div><div style="display:inline-block"><img src="/blog/uploads/alipay.jpg" alt="Luther Li 支付宝"><p>支付宝</p></div></div></div></div><div><ul class="post-copyright"><li class="post-copyright-author"><strong>本文作者： </strong>Luther Li</li><li class="post-copyright-link"><strong>本文链接：</strong> <a href="https://liliuzhu.gitee.io/blog/2019/06/Internal-technology-sharing-WebSocket.html" title="WebSocket技术分享">https://liliuzhu.gitee.io/blog/2019/06/Internal-technology-sharing-WebSocket.html</a></li><li class="post-copyright-license"><strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" rel="noopener" target="_blank"><i class="fa fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！</li></ul></div><footer class="post-footer"><div class="post-tags"><a href="/blog/tags/WebSocket/" rel="tag"># WebSocket</a> <a href="/blog/tags/js/" rel="tag"># js</a></div><div class="post-widgets"><div class="social_share"><div><div class="bdsharebuttonbox"><a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博"></a> <a href="#" class="bds_douban" data-cmd="douban" title="分享到豆瓣网"></a> <a href="#" class="bds_sqq" data-cmd="sqq" title="分享到QQ好友"></a> <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间"></a> <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信"></a> <a href="#" class="bds_tieba" data-cmd="tieba" title="分享到百度贴吧"></a> <a href="#" class="bds_twi" data-cmd="twi" title="分享到Twitter"></a> <a href="#" class="bds_fbook" data-cmd="fbook" title="分享到Facebook"></a> <a href="#" class="bds_more" data-cmd="more"></a> <a class="bds_count" data-cmd="count"></a></div><script>window._bd_share_config={common:{bdText:"",bdMini:"2",bdMiniList:!1,bdPic:""},share:{bdSize:"16",bdStyle:"0"},image:{viewList:["tsina","douban","sqq","qzone","weixin","twi","fbook"],viewText:"分享到：",viewSize:"16"}}</script><script>with(document)(0)[(getElementsByTagName("head")[0]||body).appendChild(createElement("script")).src="/static/api/js/share.js?cdnversion="+~(-new Date/36e5)]</script></div></div></div><div class="post-nav"><div class="post-nav-next post-nav-item"><a href="/blog/2019/04/JS-classicic-sorting-algorithms.html" rel="next" title="JavaScript实现经典排序算法"><i class="fa fa-chevron-left"></i> JavaScript实现经典排序算法</a></div><span class="post-nav-divider"></span><div class="post-nav-prev post-nav-item"><a href="/blog/2019/10/vue_use_babel-polyfill.html" rel="prev" title="vue中babel-polyfill的使用方法">vue中babel-polyfill的使用方法 <i class="fa fa-chevron-right"></i></a></div></div></footer></div></article></div></div><div class="comments" id="comments"></div></div><div class="sidebar-toggle"><div class="sidebar-toggle-line-wrap"><span class="sidebar-toggle-line sidebar-toggle-line-first"></span> <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span> <span class="sidebar-toggle-line sidebar-toggle-line-last"></span></div></div><aside id="sidebar" class="sidebar"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">文章目录</li><li class="sidebar-nav-overview" data-target="site-overview-wrap">站点概览</li></ul><div class="site-overview-wrap sidebar-panel"><div class="site-overview"><div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"><img class="site-author-image" itemprop="image" src="/blog/uploads/avatar.jpg" alt="Luther Li"><p class="site-author-name" itemprop="name">Luther Li</p><div class="site-description motion-element" itemprop="description">本人前端菜鸟一枚，踏在前端这条不归路上<br>渐行渐远...</div></div><nav class="site-state motion-element"><div class="site-state-item site-state-posts"><a href="/blog/archives/"><span class="site-state-item-count">8</span> <span class="site-state-item-name">日志</span></a></div><div class="site-state-item site-state-categories"><a href="/blog/categories/"><span class="site-state-item-count">8</span> <span class="site-state-item-name">分类</span></a></div><div class="site-state-item site-state-tags"><a href="/blog/tags/"><span class="site-state-item-count">10</span> <span class="site-state-item-name">标签</span></a></div></nav><div class="feed-link motion-element"><a href="/blog/atom.xml" rel="alternate"><i class="fa fa-rss"></i> RSS</a></div><div class="links-of-author motion-element"><span class="links-of-author-item"><a href="https://github.com/liliuzhu" title="GitHub &rarr; https://github.com/liliuzhu" rel="noopener" target="_blank"><i class="fa fa-fw fa-github"></i>GitHub</a> </span><span class="links-of-author-item"><a href="mailto:liliuzhu1992@163.com" title="E-Mail &rarr; mailto:liliuzhu1992@163.com" rel="noopener" target="_blank"><i class="fa fa-fw fa-envelope"></i>E-Mail</a> </span><span class="links-of-author-item"><a href="http://blog.csdn.net/yuhouxinya" title="CSDN &rarr; http://blog.csdn.net/yuhouxinya" rel="noopener" target="_blank"><i class="fa fa-fw fa-book"></i>CSDN</a> </span><span class="links-of-author-item"><a href="https://juejin.im/user/58d69e3c44d904006870487f" title="掘金 &rarr; https://juejin.im/user/58d69e3c44d904006870487f" rel="noopener" target="_blank"><i class="fa fa-fw fa-book"></i>掘金</a> </span><span class="links-of-author-item"><a href="https://liliuzhu.gitee.io" title="个人主页 &rarr; https://liliuzhu.gitee.io"><i class="fa fa-fw fa-leaf"></i>个人主页</a></span></div><div class="links-of-blogroll motion-element links-of-blogroll-inline"><div class="links-of-blogroll-title"><i class="fa fa-fw fa-link"></i> 推荐阅读</div><ul class="links-of-blogroll-list"><li class="links-of-blogroll-item"><a href="http://www.uisdc.com/" title="http://www.uisdc.com/" rel="noopener" target="_blank">优设</a></li><li class="links-of-blogroll-item"><a href="http://www.zhangxinxu.com/" title="http://www.zhangxinxu.com/" rel="noopener" target="_blank">张鑫旭</a></li><li class="links-of-blogroll-item"><a href="http://www.alloyteam.com/nav/" title="http://www.alloyteam.com/nav/" rel="noopener" target="_blank">Web前端导航</a></li><li class="links-of-blogroll-item"><a href="http://www.36zhen.com/t?id=3448" title="http://www.36zhen.com/t?id=3448" rel="noopener" target="_blank">前端书籍资料</a></li><li class="links-of-blogroll-item"><a href="http://ife.baidu.com/" title="http://ife.baidu.com/" rel="noopener" target="_blank">百度前端技术学院</a></li><li class="links-of-blogroll-item"><a href="http://wf.uisdc.com/cn/" title="http://wf.uisdc.com/cn/" rel="noopener" target="_blank">google前端开发基础</a></li></ul></div></div></div><div class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active"><div class="post-toc"><div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#科普一下通讯传输模式"><span class="nav-number">1.</span> <span class="nav-text">科普一下通讯传输模式</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#历史回顾"><span class="nav-number">2.</span> <span class="nav-text">历史回顾</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#轮询（polling）"><span class="nav-number">2.1.</span> <span class="nav-text">轮询（polling）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#长轮询（long-polling）"><span class="nav-number">2.2.</span> <span class="nav-text">长轮询（long-polling）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#长连接"><span class="nav-number">2.3.</span> <span class="nav-text">长连接</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#iframe流（streaming）"><span class="nav-number">2.3.1.</span> <span class="nav-text">iframe流（streaming）</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#事件流-EventSource（SSE-Server-Sent-Events，不能算作历史技术，属于H5范围）"><span class="nav-number">2.3.2.</span> <span class="nav-text">事件流 EventSource（SSE - Server-Sent Events，不能算作历史技术，属于H5范围）</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Flash-Socket"><span class="nav-number">2.4.</span> <span class="nav-text">Flash Socket</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#WebSocket"><span class="nav-number">3.</span> <span class="nav-text">WebSocket</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#如何使用-WebSocket"><span class="nav-number">3.1.</span> <span class="nav-text">如何使用 WebSocket</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#WebSocket-amp-EventSource-的区别"><span class="nav-number">3.1.1.</span> <span class="nav-text">WebSocket &amp; EventSource 的区别</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Web-实时推送技术的比较"><span class="nav-number">3.2.</span> <span class="nav-text">Web 实时推送技术的比较</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#服务端的WebSocket"><span class="nav-number">3.3.</span> <span class="nav-text">服务端的WebSocket</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#参考文献"><span class="nav-number">4.</span> <span class="nav-text">参考文献</span></a></li></ol></div></div></div></div></aside></div></main><footer id="footer" class="footer"><div class="footer-inner"><div class="copyright">&copy; <span itemprop="copyrightYear">2020</span> <span class="with-love" id="animate"><i class="fa fa-user"></i> </span><span class="author" itemprop="copyrightHolder">Luther Li</span> <span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-area-chart"></i> </span><span class="post-meta-item-text">站点总字数：</span> <span title="站点总字数">57k</span></div><div class="busuanzi-count"><script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><span class="post-meta-item-icon"><i class="fa fa-user"></i> </span><span class="site-uv" title="总访客量"><span class="busuanzi-value" id="busuanzi_value_site_uv"></span> </span><span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-eye"></i> </span><span class="site-pv" title="总访问量"><span class="busuanzi-value" id="busuanzi_value_site_pv"></span></span></div></div></footer><div class="back-to-top"><i class="fa fa-arrow-up"></i> <span id="scrollpercent"><span>0</span>%</span></div></div><script>"[object Function]"!==Object.prototype.toString.call(window.Promise)&&(window.Promise=null)</script><script color="0,0,255" opacity="0.5" zindex="10" count="99" src="//cdn.bootcss.com/canvas-nest.js/1.0.0/canvas-nest.min.js"></script><script src="/blog/lib/jquery/index.js?v=2.1.3"></script><script src="//cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js"></script><script src="//cdn.bootcss.com/jquery_lazyload/1.9.7/jquery.lazyload.min.js"></script><script src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script><script src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script><script src="//cdn.bootcss.com/fancybox/3.5.6/jquery.fancybox.min.js"></script><script src="/blog/js/utils.js?v=7.1.2"></script><script src="/blog/js/motion.js?v=7.1.2"></script><script src="/blog/js/affix.js?v=7.1.2"></script><script src="/blog/js/schemes/pisces.js?v=7.1.2"></script><script src="/blog/js/scrollspy.js?v=7.1.2"></script><script src="/blog/js/post-details.js?v=7.1.2"></script><script src="/blog/js/next-boot.js?v=7.1.2"></script><script src="//cdn.jsdelivr.net/npm/leancloud-storage@3/dist/av-min.js"></script><script src="//cdn.jsdelivr.net/npm/valine@1/dist/Valine.min.js"></script><script>var GUEST=["nick","mail","link"],guest="nick,mail,link";guest=guest.split(",").filter(function(e){return GUEST.indexOf(e)>-1}),new Valine({el:"#comments",verify:!1,notify:!1,appId:"fIXqVCTX6VryzoR8ENkLXThu-gzGzoHsz",appKey:"nvWdXA0OQKn58RCTSMmd5JAK",placeholder:"欢迎交流讨论...",avatar:"mm",meta:guest,pageSize:"10",visitor:!0,lang:"zh-cn"})</script><script>function proceedsearch(){$("body").append('<div class="search-popup-overlay local-search-pop-overlay"></div>').css("overflow","hidden"),$(".search-popup-overlay").click(onPopupClose),$(".popup").toggle();var e=$("#local-search-input");e.attr("autocapitalize","none"),e.attr("autocorrect","off"),e.focus()}var isfetched=!1,isXml=!0,search_path="search.xml";0===search_path.length?search_path="search.xml":/json$/i.test(search_path)&&(isXml=!1);var path="/blog/"+search_path,onPopupClose=function(e){$(".popup").hide(),$("#local-search-input").val(""),$(".search-result-list").remove(),$("#no-result").remove(),$(".local-search-pop-overlay").remove(),$("body").css("overflow","")},searchFunc=function(e,t,o){"use strict";$("body").append('<div class="search-popup-overlay local-search-pop-overlay"><div id="search-loading-icon"><i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i></div></div>').css("overflow","hidden"),$("#search-loading-icon").css("margin","20% auto 0 auto").css("text-align","center"),$.ajax({url:e,dataType:isXml?"xml":"json",async:!0,success:function(e){isfetched=!0,$(".popup").detach().appendTo(".header-inner");var n=isXml?$("entry",e).map(function(){return{title:$("title",this).text(),content:$("content",this).text(),url:$("url",this).text()}}).get():e,r=document.getElementById(t),s=document.getElementById(o),a=function(){var e=r.value.trim().toLowerCase(),t=e.split(/[\s\-]+/);t.length>1&&t.push(e);var o=[];if(e.length>0&&n.forEach(function(n){function r(t,o,n,r){for(var s=r[r.length-1],a=s.position,i=s.word,l=[],h=0;a+i.length<=n&&0!=r.length;){i===e&&h++,l.push({position:a,length:i.length});var p=a+i.length;for(r.pop();0!=r.length&&(s=r[r.length-1],a=s.position,i=s.word,p>a);)r.pop()}return c+=h,{hits:l,start:o,end:n,searchTextCount:h}}function s(e,t){var o="",n=t.start;return t.hits.forEach(function(t){o+=e.substring(n,t.position);var r=t.position+t.length;o+='<b class="search-keyword">'+e.substring(t.position,r)+"</b>",n=r}),o+=e.substring(n,t.end)}var a=!1,i=0,c=0,l=n.title.trim(),h=l.toLowerCase(),p=n.content.trim().replace(/<[^>]+>/g,""),u=p.toLowerCase(),f=decodeURIComponent(n.url).replace(/\/{2,}/g,"/"),d=[],g=[];if(""!=l&&(t.forEach(function(e){function t(e,t,o){var n=e.length;if(0===n)return[];var r=0,s=[],a=[];for(o||(t=t.toLowerCase(),e=e.toLowerCase());(s=t.indexOf(e,r))>-1;)a.push({position:s,word:e}),r=s+n;return a}d=d.concat(t(e,h,!1)),g=g.concat(t(e,u,!1))}),(d.length>0||g.length>0)&&(a=!0,i=d.length+g.length)),a){[d,g].forEach(function(e){e.sort(function(e,t){return t.position!==e.position?t.position-e.position:e.word.length-t.word.length})});var v=[];0!=d.length&&v.push(r(l,0,l.length,d));for(var $=[];0!=g.length;){var C=g[g.length-1],m=C.position,x=C.word,w=m-20,y=m+80;w<0&&(w=0),y<m+x.length&&(y=m+x.length),y>p.length&&(y=p.length),$.push(r(p,w,y,g))}$.sort(function(e,t){return e.searchTextCount!==t.searchTextCount?t.searchTextCount-e.searchTextCount:e.hits.length!==t.hits.length?t.hits.length-e.hits.length:e.start-t.start});var T=parseInt("1");T>=0&&($=$.slice(0,T));var b="";b+=0!=v.length?"<li><a href='"+f+"' class='search-result-title'>"+s(l,v[0])+"</a>":"<li><a href='"+f+"' class='search-result-title'>"+l+"</a>",$.forEach(function(e){b+="<a href='"+f+'\'><p class="search-result">'+s(p,e)+"...</p></a>"}),b+="</li>",o.push({item:b,searchTextCount:c,hitCount:i,id:o.length})}}),1===t.length&&""===t[0])s.innerHTML='<div id="no-result"><i class="fa fa-search fa-5x"></i></div>';else if(0===o.length)s.innerHTML='<div id="no-result"><i class="fa fa-frown-o fa-5x"></i></div>';else{o.sort(function(e,t){return e.searchTextCount!==t.searchTextCount?t.searchTextCount-e.searchTextCount:e.hitCount!==t.hitCount?t.hitCount-e.hitCount:t.id-e.id});var a='<ul class="search-result-list">';o.forEach(function(e){a+=e.item}),a+="</ul>",s.innerHTML=a}};r.addEventListener("input",a),$(".local-search-pop-overlay").remove(),$("body").css("overflow",""),proceedsearch()}})};$(".popup-trigger").click(function(e){e.stopPropagation(),isfetched===!1?searchFunc(path,"local-search-input","local-search-result"):proceedsearch()}),$(".popup-btn-close").click(onPopupClose),$(".popup").click(function(e){e.stopPropagation()}),$(document).on("keyup",function(e){var t=27===e.which&&$(".search-popup").is(":visible");t&&onPopupClose()})</script><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script><script type="text/javascript" src="/blog/js/core_socialist_values.js"></script><script src="/blog/live2d/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({pluginRootPath:"live2d/",pluginJsPath:"lib/",pluginModelPath:"assets/",tagMode:!1,debug:!1,model:{jsonPath:"/blog/live2d/assets/assets/haru02.model.json"},display:{position:"left",superSample:1,width:150,height:300,hOffset:10,vOffset:10},mobile:{show:!0},react:{opacityDefault:1,opacityOnHover:.2},log:!1})</script></body></html><!-- rebuild by neat -->